import threading
import subprocess
import string
import gtk
from BuildWizException import BuildWizException
from DeployerManager import DeployerManager


class BuildExecutor( threading.Thread):
    def __init__( self, buildDir, platform, command, scriptBefore, scriptAfter, outputWin, runMakeInstall, numJobs, buildFileName, wizard):
        self.stopEvent = threading.Event()
        self.__sleepPeriod = 0.1 
        self.__platf = platform
        self.__cmakeCmd = command
        self.__fullCmd = ""
        self.__buildFileLog = buildFileName
        self.__prepareFile2Log()
        self.__scriptBefore = scriptBefore
        self.__scriptAfter = scriptAfter
        self.__runMakeInstall = runMakeInstall
        self.__numMakeJobs = ""
        if numJobs > 0:
            self.__numMakeJobs = " -j" + str( numJobs)

        self.__outputWin = outputWin
        self.__buildDir = buildDir
        self.__statusValue = [ 'INITIALIZING', 'WORKING', 'DONE', 'ERROR', 'FORCED_STOP' ]
        self.__status = self.__statusValue[ 0]
        self.__eof = "__EOF__"
        self.__wiz = wizard   # to notify end of process, and change platform status
        threading.Thread.__init__( self, name=self.__platf.getPlatformName())

    def __prepareFile2Log( self):
        try:
            fd = open( self.__buildFileLog, "w") # delete its contents
            fd.close()
        except IOError:
            pass

    def __updateWorkingCopy( self):
        if self.__platf.isWorkingCopy():
            if self.__platf.isVerifySubversionUpdates():
                if not self.__platf.isWorkingCopyUpdated():
                    gtk.gdk.threads_enter()
                    dlg = gtk.MessageDialog( None, 0
                                           , gtk.MESSAGE_QUESTION
                                           , gtk.BUTTONS_YES_NO
                                           , "The working copy is out-of-date, do you want to update it?")
                    dlg.set_title( self.__platf.getID())
                    response = dlg.run()
                    dlg.destroy() 
                    if response == gtk.RESPONSE_YES:
                        self.__outputWin.appendText( "Updating working copy...\n")
                        cmdList = []
                        cmdList.append( "cd " + self.__platf.getProjectDir())
                        cmdList.append( "svn update")
                        cmd = " && ".join( cmdList)
                        proc = subprocess.Popen( cmd, shell=True
                                               , stdin=subprocess.PIPE
                                               , stdout=subprocess.PIPE
                                               , stderr=subprocess.STDOUT) # send errors to stdout
                        data = proc.stdout.readline()
                        while data != "":
                            self.__outputWin.appendText( data)
                            data = proc.stdout.readline()

                        self.__outputWin.appendText( "done...\n\n")
                    gtk.gdk.threads_leave()

    def run( self):
        self.__outputWin.show()
        self.__platf.setStatus( 'STATUS_BUILD_IN_PROGRESS')
        try:
            self.__status = self.__statusValue[ 1]
            self.__updateWorkingCopy()
            self.__runCommand()
            if self.__status == 'DONE':
                gtk.gdk.threads_enter()
                self.__outputWin.appendText( "\n\n=*=*= FINISHED SUCCESSFULY =*=*=\n")
                self.__status = self.__statusValue[ 2]
                self.__outputWin.generateReport()
                #gtk.gdk.threads_leave()
                self.__platf.setStatus( 'STATUS_BUILD_DONE')
                if self.__outputWin.isBtnDeployActivated():
                    self.__deployIt()
                gtk.gdk.threads_leave()
        except BuildWizException, e:
            gtk.gdk.threads_enter()
            self.__outputWin.appendText( e.getStrError())
            gtk.gdk.threads_leave()
            self.__platf.setStatus( 'STATUS_BUILD_ERROR')
        finally:
            self.__outputWin.disableBtnStop()
            self.__outputWin.enableBtnSave()

    def join( self, timeout = None):
        self.stopEvent.set()
        threading.Thread.join( self, timeout)
        self.__status = self.__statusValue[ 4]
        self.__platf.setStatus( 'STATUS_BUILD_FORCED_STOP')

    def __deployIt( self):
        platfname = self.__platf.getPlatformName()
        info = self.__wiz.getDeployInfo( platfname)
        if len( info) > 0:
            deployer = DeployerManager( self.__wiz, self.__outputWin, platfname, info)

    def __assemblyFullCommand( self):
        self.__fullCmd = []
        logstep = "date +" + self.__wiz.datetimeFrmt + " | xargs -i echo {} "
        tofile = " [OK] >> " + self.__buildFileLog
        self.__fullCmd.append( logstep + self.__wiz.buildTags[ "start"] + tofile)

        if len( self.__scriptBefore) > 0:
            self.__fullCmd.append(  "source " + self.__scriptBefore)
            self.__fullCmd.append( logstep + self.__wiz.buildTags[ "before"] + tofile)

        self.__fullCmd.append( "cd " + self.__buildDir)
        self.__fullCmd.append( logstep + self.__wiz.buildTags[ "cd"] + tofile)
        self.__fullCmd.append( self.__cmakeCmd)
        self.__fullCmd.append( logstep + self.__wiz.buildTags[ "cmake"] + tofile)
        self.__fullCmd.append( "make" + self.__numMakeJobs)
        self.__fullCmd.append( logstep + self.__wiz.buildTags[ "make"] + tofile)

        if self.__runMakeInstall:
            self.__fullCmd.append( "make install")
            self.__fullCmd.append( logstep + self.__wiz.buildTags[ "install"] + tofile)

        if len( self.__scriptAfter) > 0:
            self.__fullCmd.append( "source " + self.__scriptAfter)
            self.__fullCmd.append( logstep + self.__wiz.buildTags[ "after"] + tofile)

        self.__fullCmd.append( logstep + self.__wiz.buildTags[ "end"] + tofile)
        self.__fullCmd.append( "echo " + self.__eof)
        return " && ".join( self.__fullCmd)
        

    def __runCommand( self):
        if self.__status == 'FORCED_STOP':
            return
        
        cmd = self.__assemblyFullCommand()
        gtk.gdk.threads_enter()
        self.__outputWin.appendText( "==> Shell command: [" + cmd + "]\n\n")
        gtk.gdk.threads_leave()
        proc = subprocess.Popen( cmd, shell=True
                               , stdin=subprocess.PIPE
                               , stdout=subprocess.PIPE
                               , stderr=subprocess.STDOUT) # send errors to stdout

        lastLineInserted = ""
        while not self.stopEvent.isSet():
            gtk.gdk.threads_enter()
            data = proc.stdout.readline()
            #if string.find( data, self.__eof) > -1:
            #    self.__status = self.__statusValue[ 2]
            #    self.stopEvent.set()
            #elif len( data) == 0: # it's an error
            if len( data) == 0:
                if string.find( lastLineInserted, self.__eof) > -1:
                    self.__status = self.__statusValue[ 2]
                else:
                    self.__status = self.__statusValue[ 3]

                self.stopEvent.set()
            else:
                self.__outputWin.appendText( data)
                lastLineInserted = data

            gtk.gdk.threads_leave()
            self.stopEvent.wait( self.__sleepPeriod) 

        if self.__status == 'ERROR':
            raise BuildWizException( "\n[ERROR] There is something wrong with the command: \n\t" + cmd + "\n")

